/**
 * at.js - 1.5.3
 * Copyright (c) 2017 chord.luo <chord.luo@gmail.com>;
 * Homepage: http://ichord.github.com/At.js
 * License: MIT
 */
(function (root, factory) {
	if (typeof define === 'function' && define.amd) {
		// AMD. Register as an anonymous module unless amdModuleId is set
		define(["jquery"], function (a0) {
			return (factory(a0));
		});
	} else if (typeof exports === 'object') {
		// Node. Does not work with strict CommonJS, but
		// only CommonJS-like environments that support module.exports,
		// like Node.
		module.exports = factory(require("jquery"));
	} else {
		factory(jQuery);
	}
}(this, function ($) {
	var DEFAULT_CALLBACKS, KEY_CODE;

	KEY_CODE = {
		ESC: 27,
		TAB: 9,
		ENTER: 13,
		CTRL: 17,
		A: 65,
		P: 80,
		N: 78,
		LEFT: 37,
		UP: 38,
		RIGHT: 39,
		DOWN: 40,
		BACKSPACE: 8,
		SPACE: 32,
		DELETE: 46
	};

	DEFAULT_CALLBACKS = {
		beforeSave: function (data) {
			return Controller.arrayToDefaultHash(data);
		},
		matcher: function (flag, subtext, should_startWithSpace, acceptSpaceBar) {
			var _a, _y, match, regexp, space;
			flag = flag.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
			_a = decodeURI("%C3%80");
			_y = decodeURI("%C3%BF");
			space = acceptSpaceBar ? "\ " : "";
			regexp = new RegExp(flag + "([A-Za-z" + _a + "-" + _y + "0-9_" + space + "\'\.\+\-]*)$|" + flag + "([^\\x00-\\xff]*)$", 'gi');

			match = regexp.exec(subtext);
			if (match) {
				return match[2] || match[1];
			} else {
				return null;
			}
		},
		filter: function (query, data, searchKey) {
			var _results, i, item, len;
			_results = [];
			for (i = 0, len = data.length; i < len; i++) {
				item = data[i];
				if (~new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase())) {
					_results.push(item);
				}
			}
			return _results;
		},
		remoteFilter: null,
		sorter: function (query, items, searchKey) {
			var _results, i, item, len;
			if (!query) {
				return items;
			}
			_results = [];
			for (i = 0, len = items.length; i < len; i++) {
				item = items[i];
				item.atwho_order = new String(item[searchKey]).toLowerCase().indexOf(query.toLowerCase());
				if (item.atwho_order > -1) {
					_results.push(item);
				}
			}
			return _results.sort(function (a, b) {
				return a.atwho_order - b.atwho_order;
			});
		},
		tplEval: function (tpl, map) {
			var error, error1, template;
			template = tpl;
			try {
				if (typeof tpl !== 'string') {
					template = tpl(map);
				}
				return template.replace(/\$\{([^\}]*)\}/g, function (tag, key, pos) {
					return map[key];
				});
			} catch (error1) {
				error = error1;
				return "";
			}
		},
		highlighter: function (li, query) {
			var regexp;
			if (!query) {
				return li;
			}
			regexp = new RegExp(">\\s*([^\<]*?)(" + query.replace("+", "\\+") + ")([^\<]*)\\s*<", 'ig');
			return li.replace(regexp, function (str, $1, $2, $3) {
				return '> ' + $1 + '<strong>' + $2 + '</strong>' + $3 + ' <';
			});
		},
		beforeInsert: function (value, $li, e) {
			return value;
		},
		beforeReposition: function (offset) {
			return offset;
		},
		afterMatchFailed: function (at, el) { }
	};

	var App;

	App = (function () {
		function App(inputor) {
			this.currentFlag = null;
			this.controllers = {};
			this.aliasMaps = {};
			this.$inputor = $(inputor);
			this.setupRootElement();
			this.listen();
		}

		App.prototype.createContainer = function (doc) {
			var ref;
			if ((ref = this.$el) != null) {
				ref.remove();
			}
			return $(doc.body).append(this.$el = $("<div class='atwho-container'></div>"));
		};

		App.prototype.setupRootElement = function (iframe, asRoot) {
			var error, error1;
			if (asRoot == null) {
				asRoot = false;
			}
			if (iframe) {
				this.window = iframe.contentWindow;
				this.document = iframe.contentDocument || this.window.document;
				this.iframe = iframe;
			} else {
				this.document = this.$inputor[0].ownerDocument;
				this.window = this.document.defaultView || this.document.parentWindow;
				try {
					this.iframe = this.window.frameElement;
				} catch (error1) {
					error = error1;
					this.iframe = null;
					if ($.fn.atwho.debug) {
						throw new Error("iframe auto-discovery is failed.\nPlease use `setIframe` to set the target iframe manually.\n" + error);
					}
				}
			}
			return this.createContainer((this.iframeAsRoot = asRoot) ? this.document : document);
		};

		App.prototype.controller = function (at) {
			var c, current, currentFlag, ref;
			if (this.aliasMaps[at]) {
				current = this.controllers[this.aliasMaps[at]];
			} else {
				ref = this.controllers;
				for (currentFlag in ref) {
					c = ref[currentFlag];
					if (currentFlag === at) {
						current = c;
						break;
					}
				}
			}
			if (current) {
				return current;
			} else {
				return this.controllers[this.currentFlag];
			}
		};

		App.prototype.setContextFor = function (at) {
			this.currentFlag = at;
			return this;
		};

		App.prototype.reg = function (flag, setting) {
			var base, controller;
			controller = (base = this.controllers)[flag] || (base[flag] = this.$inputor.is('[contentEditable]') ? new EditableController(this, flag) : new TextareaController(this, flag));
			if (setting.alias) {
				this.aliasMaps[setting.alias] = flag;
			}
			controller.init(setting);
			return this;
		};

		/* 监听 */
		App.prototype.listen = function () {
			return this.$inputor.on('compositionstart', (function (_this) {
				return function (e) {
					var ref;
					if ((ref = _this.controller()) != null) {
						ref.view.hide();
					}
					_this.isComposing = true;
					return null;
				};
			})(this)).on('compositionend', (function (_this) {
				return function (e) {
					_this.isComposing = false;
					setTimeout(function (e) {
						return _this.dispatch(e);
					});
					return null;
				};
			})(this)).on('keyup.atwhoInner', (function (_this) {
				return function (e) {
					return _this.onKeyup(e);
				};
			})(this)).on('keydown.atwhoInner', (function (_this) {
				return function (e) {
					return _this.onKeydown(e);
				};
			})(this)).on('blur.atwhoInner', (function (_this) {
				return function (e) {
					var c;
					if (c = _this.controller()) {
						c.expectedQueryCBId = null;
						return c.view.hide(e, c.getOpt("displayTimeout"));
					}
				};
			})(this)).on('click.atwhoInner', (function (_this) {
				return function (e) {
					return _this.dispatch(e);
				};
			})(this)).on('scroll.atwhoInner', (function (_this) {
				return function () {
					var lastScrollTop;
					lastScrollTop = _this.$inputor.scrollTop();
					return function (e) {
						var currentScrollTop, ref;
						currentScrollTop = e.target.scrollTop;
						if (lastScrollTop !== currentScrollTop) {
							if ((ref = _this.controller()) != null) {
								ref.view.hide(e);
							}
						}
						lastScrollTop = currentScrollTop;
						return true;
					};
				};
			})(this)());
		};

		App.prototype.shutdown = function () {
			var _, c, ref;
			ref = this.controllers;
			for (_ in ref) {
				c = ref[_];
				c.destroy();
				delete this.controllers[_];
			}
			this.$inputor.off('.atwhoInner');
			return this.$el.remove();
		};

		App.prototype.dispatch = function (e) {
			var r, c, ref, results;
			ref = this.controllers;

			results = [];
			for (r in ref) {
				c = ref[r];
				results.push(c.lookUp(e));
			}

			return results;
		};

		App.prototype.onKeyup = function (e) {
			var ref;
			switch (e.keyCode) {
				case KEY_CODE.ESC:
					e.preventDefault();
					if ((ref = this.controller()) != null) {
						ref.view.hide();
					}
					break;
				case KEY_CODE.DOWN:
				case KEY_CODE.UP:
				case KEY_CODE.CTRL:
				case KEY_CODE.ENTER:
					$.noop();
					break;
				case KEY_CODE.P:
				case KEY_CODE.N:
					if (!e.ctrlKey) {
						this.dispatch(e);
					}
					break;
				default:
					this.dispatch(e);
			}
			if (this.controller()) {
				this.controller().lookUpFrontAndBackAt();
			}
		};

		App.prototype.onKeydown = function (e) {

			var ref, view;

			view = (ref = this.controller()) != null ? ref.view : void 0;

			if (!(view && view.visible()) && e.keyCode != KEY_CODE.BACKSPACE && e.keyCode != KEY_CODE.DELETE) {
				return;
			}
			switch (e.keyCode) {
				case KEY_CODE.ESC:
					e.preventDefault();
					view.hide(e);
					break;
				case KEY_CODE.UP:
					e.preventDefault();
					view.prev();
					break;
				case KEY_CODE.DOWN:
					e.preventDefault();
					view.next();
					break;
				case KEY_CODE.P:
					if (!e.ctrlKey) {
						return;
					}
					e.preventDefault();
					view.prev();
					break;
				case KEY_CODE.N:
					if (!e.ctrlKey) {
						return;
					}
					e.preventDefault();
					view.next();
					break;
				case KEY_CODE.TAB:
				case KEY_CODE.ENTER:
				case KEY_CODE.SPACE:
					if (!view.visible()) {
						return;
					}
					if (!this.controller().getOpt('spaceSelectsMatch') && e.keyCode === KEY_CODE.SPACE) {
						return;
					}
					if (!this.controller().getOpt('tabSelectsMatch') && e.keyCode === KEY_CODE.TAB) {
						return;
					}
					if (view.highlighted()) {
						e.preventDefault();
						view.choose(e);
					} else {
						view.hide(e);
					}
					break;
				case KEY_CODE.DELETE:
					var contrl = this.controller();
					if (contrl && contrl.backAt) {
						e.preventDefault();
						contrl.deleteAt(false);
					} else {
						var results = this.dispatch(e);
					}
					break;
				case KEY_CODE.BACKSPACE:
					/* 删除键删除整个名字 */

					/* 向后删除掉匹配的@ */
					var contrl = this.controller();
					if (contrl && contrl.frontAt) {
						e.preventDefault();
						contrl.deleteAt(true);
					} else {
						var results = this.dispatch(e);
					}

					break;
				default:
					$.noop();
			}
		};
		return App;
	})();

	var Controller,
		slice = [].slice;

	Controller = (function () {
		Controller.prototype.uid = function () {
			return (Math.random().toString(16) + "000000000").substr(2, 8) + (new Date().getTime());
		};

		function Controller(app, at1) {
			this.app = app;
			this.at = at1;
			this.$inputor = this.app.$inputor;
			this.id = this.$inputor[0].id || this.uid();
			this.expectedQueryCBId = null;
			this.setting = null;
			this.query = null;
			this.pos = 0;
			this.range = null;
			if ((this.$el = $("#atwho-ground-" + this.id, this.app.$el)).length === 0) {
				this.app.$el.append(this.$el = $("<div id='atwho-ground-" + this.id + "'></div>"));
			}
			this.model = new Model(this);
			this.view = new View(this);
		}

		Controller.prototype.init = function (setting) {
			this.setting = $.extend({}, this.setting || $.fn.atwho["default"], setting);
			this.view.init();
			return this.model.reload(this.setting.data);
		};

		Controller.prototype.isValidAtUser = function (name) {
			var searchKey = this.setting.searchKey;
			return this.setting.data.filter(function (u) {
				return u[searchKey] == name;
			}).length > 0;
		};

		Controller.prototype.destroy = function () {
			this.trigger('beforeDestroy');
			this.model.destroy();
			this.view.destroy();
			return this.$el.remove();
		};

		Controller.prototype.callDefault = function () {
			var args, error, error1, funcName;
			funcName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
			try {
				return DEFAULT_CALLBACKS[funcName].apply(this, args);
			} catch (error1) {
				error = error1;
				return $.error(error + " Or maybe At.js doesn't have function " + funcName);
			}
		};

		Controller.prototype.trigger = function (name, data) {
			var alias, eventName;
			if (data == null) {
				data = [];
			}
			data.push(this);
			alias = this.getOpt('alias');
			eventName = alias ? name + "-" + alias + ".atwho" : name + ".atwho";
			return this.$inputor.trigger(eventName, data);
		};

		Controller.prototype.callbacks = function (funcName) {
			return this.getOpt("callbacks")[funcName] || DEFAULT_CALLBACKS[funcName];
		};

		Controller.prototype.getOpt = function (at, default_value) {
			var e, error1;
			try {
				return this.setting[at];
			} catch (error1) {
				e = error1;
				return null;
			}
		};

		Controller.prototype.insertContentFor = function ($li) {
			var data, tpl;
			tpl = this.getOpt('insertTpl');
			data = $.extend({}, $li.data('item-data'), {
				'atwho-at': this.at
			});
			return this.callbacks("tplEval").call(this, tpl, data, "onInsert");
		};

		Controller.prototype.renderView = function (data) {
			var searchKey;
			searchKey = this.getOpt("searchKey");
			data = this.callbacks("sorter").call(this, this.query.text, data.slice(0, 1001), searchKey);

			return this.view.render(data.slice(0, this.getOpt('limit')));
		};

		Controller.arrayToDefaultHash = function (data) {
			var i, item, len, results;
			if (!$.isArray(data)) {
				return data;
			}
			results = [];
			for (i = 0, len = data.length; i < len; i++) {
				item = data[i];
				if ($.isPlainObject(item)) {
					results.push(item);
				} else {
					results.push({
						name: item
					});
				}
			}
			return results;
		};

		Controller.prototype.deleteAt = function (isBackspace) {
			if (isBackspace) {
				this.$inputor[0].setSelectionRange(this.atCursorPosition - this.frontAt.length, this.atCursorPosition);
			} else {
				this.$inputor[0].setSelectionRange(this.atCursorPosition, this.atCursorPosition + this.backAt.length);
			}
			this.$inputor.focus();

			document.execCommand("delete", false);
		}

		/* 查找前后是否存在合法的@,（先后删除键或者向前删除键按下的情况） */
		Controller.prototype.lookUpFrontAndBackAt = function () {
			/* 当前的光标位置处，截断成两节 */
			var caretPos = this.$inputor.caret('pos', {
				iframe: this.app.iframe
			});
			var content = this.$inputor.val();
			var frontValue = content.slice(0, caretPos);
			var backValue = content.slice(caretPos);

			/* backspace 键备用 */
			var isFrontAt = /@([^\s]*[\s])$/.exec(frontValue);
			if (isFrontAt && this.isValidAtUser(isFrontAt[1].trim())) {
				this.frontAt = `@${isFrontAt[1]}`;
			} else {
				this.frontAt = null;
			}

			/* delete 键备用 */
			var isBackAt = /^@([^\s@]*[\s])/.exec(backValue);
			if (isBackAt && this.isValidAtUser(isBackAt[1].trim())) {
				this.backAt = `@${isBackAt[1]}`;
			} else {
				this.backAt = null;
			}

			if (this.frontAt || this.backAt) {
				this.atCursorPosition = caretPos;
			}
		}

		/* 这里查找被@人 */
		Controller.prototype.lookUp = function (e) {
			var query, wait;
			if (e && e.type === 'click' && !this.getOpt('lookUpOnClick')) {
				return;
			}
			if (this.getOpt('suspendOnComposing') && this.app.isComposing) {
				return;
			}
			query = this.catchQuery(e);

			this.lookUpFrontAndBackAt();

			if (!query) {
				this.expectedQueryCBId = null;
				return query;
			}

			this.app.setContextFor(this.at);
			if (wait = this.getOpt('delay')) {
				this._delayLookUp(query, wait);
			} else {
				this._lookUp(query);
			}

			var content = this.$inputor.val();
			var frontValue = content.slice(0, query.headPos - 1);
			var nextValue = content.slice(query.headPos - 1);

			var searchKey = this.setting;

			/* start 整体处理@ */
			var isAt = /^@([^\s@]*)[\s]/.exec(nextValue);
			/* 有可能是合法的@ (被@的人存在)*/
			if (isAt) {
				var name = isAt[1];


				if (this.isValidAtUser(name)) {
					/* 以空格结尾是合法的A */
					if (e.keyCode == KEY_CODE.LEFT) {
						this.$inputor.caret('pos', frontValue.length, {
							iframe: this.app.iframe
						});
						this.lookUpFrontAndBackAt();
					} else {
						this.$inputor.caret('pos', frontValue.length + isAt[0].length, {
							iframe: this.app.iframe
						});
						this.lookUpFrontAndBackAt();
					}
					this.view.hide();
				}
			}

			/* end 整体处理@ */
			return query;
		};

		Controller.prototype._delayLookUp = function (query, wait) {
			var now, remaining;
			now = Date.now ? Date.now() : new Date().getTime();
			this.previousCallTime || (this.previousCallTime = now);
			remaining = wait - (now - this.previousCallTime);
			if ((0 < remaining && remaining < wait)) {
				this.previousCallTime = now;
				this._stopDelayedCall();
				return this.delayedCallTimeout = setTimeout((function (_this) {
					return function () {
						_this.previousCallTime = 0;
						_this.delayedCallTimeout = null;
						return _this._lookUp(query);
					};
				})(this), wait);
			} else {
				this._stopDelayedCall();
				if (this.previousCallTime !== now) {
					this.previousCallTime = 0;
				}
				return this._lookUp(query);
			}
		};

		Controller.prototype._stopDelayedCall = function () {
			if (this.delayedCallTimeout) {
				clearTimeout(this.delayedCallTimeout);
				return this.delayedCallTimeout = null;
			}
		};

		Controller.prototype._generateQueryCBId = function () {
			return {};
		};

		Controller.prototype._lookUp = function (query) {
			var _callback;
			_callback = function (queryCBId, data) {
				if (queryCBId !== this.expectedQueryCBId) {
					return;
				}
				if (data && data.length > 0) {
					return this.renderView(this.constructor.arrayToDefaultHash(data));
				} else {
					return this.view.hide();
				}
			};
			this.expectedQueryCBId = this._generateQueryCBId();
			return this.model.query(query.text, $.proxy(_callback, this, this.expectedQueryCBId));
		};

		return Controller;

	})();

	var TextareaController,
		extend = function (child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
		hasProp = {}.hasOwnProperty;

	TextareaController = (function (superClass) {
		extend(TextareaController, superClass);

		function TextareaController() {
			return TextareaController.__super__.constructor.apply(this, arguments);
		}

		/*  */
		TextareaController.prototype.catchQuery = function () {
			var caretPos, content, end, isString, query, start, subtext;
			content = this.$inputor.val();
			caretPos = this.$inputor.caret('pos', {
				iframe: this.app.iframe
			});
			subtext = content.slice(0, caretPos);
			query = this.callbacks("matcher").call(this, this.at, subtext, this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
			isString = typeof query === 'string';
			if (isString && query.length < this.getOpt('minLen', 0)) {
				return;
			}
			if (isString && query.length <= this.getOpt('maxLen', 20)) {
				start = caretPos - query.length;
				end = start + query.length;
				this.pos = start;
				query = {
					'text': query,
					'headPos': start,
					'endPos': end
				};
				this.trigger("matched", [this.at, query.text]);
			} else {
				query = null;
				this.view.hide();
			}

			return this.query = query;
		};

		TextareaController.prototype.rect = function () {
			var c, iframeOffset, scaleBottom;
			if (!(c = this.$inputor.caret('offset', this.pos - 1, {
				iframe: this.app.iframe
			}))) {
				return;
			}
			if (this.app.iframe && !this.app.iframeAsRoot) {
				iframeOffset = $(this.app.iframe).offset();
				c.left += iframeOffset.left;
				c.top += iframeOffset.top;
			}
			scaleBottom = this.app.document.selection ? 0 : 2;
			return {
				left: c.left,
				top: c.top,
				bottom: c.top + c.height + scaleBottom
			};
		};

		TextareaController.prototype.insert = function (content, $li) {
			var $inputor, source, startStr, suffix, text;
			$inputor = this.$inputor;
			source = $inputor.val();
			startStr = source.slice(0, Math.max(this.query.headPos - this.at.length, 0));
			suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || " ";
			content += suffix;
			text = "" + startStr + content + (source.slice(this.query['endPos'] || 0));
			$inputor.val(text);
			$inputor.caret('pos', startStr.length + content.length, {
				iframe: this.app.iframe
			});
			if (!$inputor.is(':focus')) {
				$inputor.focus();
			}
			this.lookUp();
			return $inputor.change();
		};

		return TextareaController;

	})(Controller);

	var EditableController,
		extend = function (child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
		hasProp = {}.hasOwnProperty;

	EditableController = (function (superClass) {
		extend(EditableController, superClass);

		function EditableController() {
			return EditableController.__super__.constructor.apply(this, arguments);
		}

		EditableController.prototype._getRange = function () {
			var sel;
			sel = this.app.window.getSelection();
			if (sel.rangeCount > 0) {
				return sel.getRangeAt(0);
			}
		};

		EditableController.prototype._setRange = function (position, node, range) {
			if (range == null) {
				range = this._getRange();
			}
			if (!(range && node)) {
				return;
			}
			node = $(node)[0];
			if (position === 'after') {
				range.setEndAfter(node);
				range.setStartAfter(node);
			} else {
				range.setEndBefore(node);
				range.setStartBefore(node);
			}
			range.collapse(false);
			return this._clearRange(range);
		};

		EditableController.prototype._clearRange = function (range) {
			var sel;
			if (range == null) {
				range = this._getRange();
			}
			sel = this.app.window.getSelection();
			if (this.ctrl_a_pressed == null) {
				sel.removeAllRanges();
				return sel.addRange(range);
			}
		};

		EditableController.prototype._movingEvent = function (e) {
			var ref;
			return e.type === 'click' || ((ref = e.which) === KEY_CODE.RIGHT || ref === KEY_CODE.LEFT || ref === KEY_CODE.UP || ref === KEY_CODE.DOWN);
		};

		EditableController.prototype._unwrap = function (node) {
			var next;
			node = $(node).unwrap().get(0);
			if ((next = node.nextSibling) && next.nodeValue) {
				node.nodeValue += next.nodeValue;
				$(next).remove();
			}
			return node;
		};

		EditableController.prototype.catchQuery = function (e) {
			var $inserted, $query, _range, index, inserted, isString, lastNode, matched, offset, query, query_content, range;
			if (!(range = this._getRange())) {
				return;
			}
			if (!range.collapsed) {
				return;
			}
			if (e.which === KEY_CODE.ENTER) {
				($query = $(range.startContainer).closest('.atwho-query')).contents().unwrap();
				if ($query.is(':empty')) {
					$query.remove();
				}
				($query = $(".atwho-query", this.app.document)).text($query.text()).contents().last().unwrap();
				this._clearRange();
				return;
			}
			if (/firefox/i.test(navigator.userAgent)) {
				if ($(range.startContainer).is(this.$inputor)) {
					this._clearRange();
					return;
				}
				if (e.which === KEY_CODE.BACKSPACE && range.startContainer.nodeType === document.ELEMENT_NODE && (offset = range.startOffset - 1) >= 0) {
					_range = range.cloneRange();
					_range.setStart(range.startContainer, offset);
					if ($(_range.cloneContents()).contents().last().is('.atwho-inserted')) {
						inserted = $(range.startContainer).contents().get(offset);
						this._setRange('after', $(inserted).contents().last());
					}
				} else if (e.which === KEY_CODE.LEFT && range.startContainer.nodeType === document.TEXT_NODE) {
					$inserted = $(range.startContainer.previousSibling);
					if ($inserted.is('.atwho-inserted') && range.startOffset === 0) {
						this._setRange('after', $inserted.contents().last());
					}
				}
			}
			$(range.startContainer).closest('.atwho-inserted').addClass('atwho-query').siblings().removeClass('atwho-query');
			if (($query = $(".atwho-query", this.app.document)).length > 0 && $query.is(':empty') && $query.text().length === 0) {
				$query.remove();
			}
			if (!this._movingEvent(e)) {
				$query.removeClass('atwho-inserted');
			}
			if ($query.length > 0) {
				switch (e.which) {
					case KEY_CODE.LEFT:
						this._setRange('before', $query.get(0), range);
						$query.removeClass('atwho-query');
						return;
					case KEY_CODE.RIGHT:
						this._setRange('after', $query.get(0).nextSibling, range);
						$query.removeClass('atwho-query');
						return;
				}
			}
			if ($query.length > 0 && (query_content = $query.attr('data-atwho-at-query'))) {
				$query.empty().html(query_content).attr('data-atwho-at-query', null);
				this._setRange('after', $query.get(0), range);
			}
			_range = range.cloneRange();
			_range.setStart(range.startContainer, 0);
			matched = this.callbacks("matcher").call(this, this.at, _range.toString(), this.getOpt('startWithSpace'), this.getOpt("acceptSpaceBar"));
			isString = typeof matched === 'string';
			if ($query.length === 0 && isString && (index = range.startOffset - this.at.length - matched.length) >= 0) {
				range.setStart(range.startContainer, index);
				$query = $('<span/>', this.app.document).attr(this.getOpt("editableAtwhoQueryAttrs")).addClass('atwho-query');
				range.surroundContents($query.get(0));
				lastNode = $query.contents().last().get(0);
				if (lastNode) {
					if (/firefox/i.test(navigator.userAgent)) {
						range.setStart(lastNode, lastNode.length);
						range.setEnd(lastNode, lastNode.length);
						this._clearRange(range);
					} else {
						this._setRange('after', lastNode, range);
					}
				}
			}
			if (isString && matched.length < this.getOpt('minLen', 0)) {
				return;
			}
			if (isString && matched.length <= this.getOpt('maxLen', 20)) {
				query = {
					text: matched,
					el: $query
				};
				this.trigger("matched", [this.at, query.text]);
				return this.query = query;
			} else {
				this.view.hide();
				this.query = {
					el: $query
				};
				if ($query.text().indexOf(this.at) >= 0) {
					if (this._movingEvent(e) && $query.hasClass('atwho-inserted')) {
						$query.removeClass('atwho-query');
					} else if (false !== this.callbacks('afterMatchFailed').call(this, this.at, $query)) {
						this._setRange("after", this._unwrap($query.text($query.text()).contents().first()));
					}
				}
				return null;
			}
		};

		EditableController.prototype.rect = function () {
			var $iframe, iframeOffset, rect;
			rect = this.query.el.offset();
			if (!rect) {
				return;
			}
			if (this.app.iframe && !this.app.iframeAsRoot) {
				iframeOffset = ($iframe = $(this.app.iframe)).offset();
				rect.left += iframeOffset.left - this.$inputor.scrollLeft();
				rect.top += iframeOffset.top - this.$inputor.scrollTop();
			}
			rect.bottom = rect.top + this.query.el.height();
			return rect;
		};

		EditableController.prototype.insert = function (content, $li) {
			var data, range, suffix, suffixNode;
			if (!this.$inputor.is(':focus')) {
				this.$inputor.focus();
			}
			suffix = (suffix = this.getOpt('suffix')) === "" ? suffix : suffix || "\u00A0";
			data = $li.data('item-data');
			this.query.el.removeClass('atwho-query').addClass('atwho-inserted').html(content).attr('data-atwho-at-query', "" + data['atwho-at'] + this.query.text).attr('contenteditable', "false");
			if (range = this._getRange()) {
				if (this.query.el.length) {
					range.setEndAfter(this.query.el[0]);
				}
				range.collapse(false);
				range.insertNode(suffixNode = this.app.document.createTextNode("" + suffix));
				this._setRange('after', suffixNode, range);
			}
			if (!this.$inputor.is(':focus')) {
				this.$inputor.focus();
			}
			return this.$inputor.change();
		};

		return EditableController;

	})(Controller);

	var Model;

	Model = (function () {
		function Model(context) {
			this.context = context;
			this.at = this.context.at;
			this.storage = this.context.$inputor;
		}

		Model.prototype.destroy = function () {
			return this.storage.data(this.at, null);
		};

		Model.prototype.saved = function () {
			return this.fetch() > 0;
		};

		Model.prototype.query = function (query, callback) {
			var _remoteFilter, data, searchKey;
			data = this.fetch();
			searchKey = this.context.getOpt("searchKey");
			data = this.context.callbacks('filter').call(this.context, query, data, searchKey) || [];
			_remoteFilter = this.context.callbacks('remoteFilter');
			if (data.length > 0 || (!_remoteFilter && data.length === 0)) {
				return callback(data);
			} else {
				return _remoteFilter.call(this.context, query, callback);
			}
		};

		Model.prototype.fetch = function () {
			return this.storage.data(this.at) || [];
		};

		Model.prototype.save = function (data) {
			return this.storage.data(this.at, this.context.callbacks("beforeSave").call(this.context, data || []));
		};

		Model.prototype.load = function (data) {
			if (!(this.saved() || !data)) {
				return this._load(data);
			}
		};

		Model.prototype.reload = function (data) {
			return this._load(data);
		};

		Model.prototype._load = function (data) {
			if (typeof data === "string") {
				return $.ajax(data, {
					dataType: "json"
				}).done((function (_this) {
					return function (data) {
						return _this.save(data);
					};
				})(this));
			} else {
				return this.save(data);
			}
		};

		return Model;

	})();

	var View;

	View = (function () {
		function View(context) {
			this.context = context;
			this.$el = $("<div class='atwho-view'><ul class='atwho-view-ul'></ul></div>");
			this.$elUl = this.$el.children();
			this.timeoutID = null;
			this.context.$el.append(this.$el);
			this.bindEvent();
		}

		View.prototype.init = function () {
			var header_tpl, id;
			id = this.context.getOpt("alias") || this.context.at.charCodeAt(0);
			header_tpl = this.context.getOpt("headerTpl");
			if (header_tpl && this.$el.children().length === 1) {
				this.$el.prepend(header_tpl);
			}
			return this.$el.attr({
				'id': "at-view-" + id
			});
		};

		View.prototype.destroy = function () {
			return this.$el.remove();
		};

		View.prototype.bindEvent = function () {
			var $menu, lastCoordX, lastCoordY;
			$menu = this.$el.find('ul');
			lastCoordX = 0;
			lastCoordY = 0;
			return $menu.on('mousemove.atwho-view', 'li', (function (_this) {
				return function (e) {
					var $cur;
					if (lastCoordX === e.clientX && lastCoordY === e.clientY) {
						return;
					}
					lastCoordX = e.clientX;
					lastCoordY = e.clientY;
					$cur = $(e.currentTarget);
					if ($cur.hasClass('cur')) {
						return;
					}
					$menu.find('.cur').removeClass('cur');
					return $cur.addClass('cur');
				};
			})(this)).on('click.atwho-view', 'li', (function (_this) {
				return function (e) {
					$menu.find('.cur').removeClass('cur');
					$(e.currentTarget).addClass('cur');
					_this.choose(e);
					return e.preventDefault();
				};
			})(this));
		};

		View.prototype.visible = function () {
			return $.expr.filters.visible(this.$el[0]);
		};

		View.prototype.highlighted = function () {
			return this.$el.find(".cur").length > 0;
		};

		View.prototype.choose = function (e) {
			var $li, content;
			if (($li = this.$el.find(".cur")).length) {
				content = this.context.insertContentFor($li);
				this.context._stopDelayedCall();
				this.context.insert(this.context.callbacks("beforeInsert").call(this.context, content, $li, e), $li);
				this.context.trigger("inserted", [$li, e]);
				this.hide(e);
			}
			if (this.context.getOpt("hideWithoutSuffix")) {
				return this.stopShowing = true;
			}
		};

		View.prototype.reposition = function (rect) {
			var _window, offset, overflowOffset, ref;
			_window = this.context.app.iframeAsRoot ? this.context.app.window : window;
			if (rect.bottom + this.$el.height() - $(_window).scrollTop() > $(_window).height()) {
				rect.bottom = rect.top - this.$el.height();
			}
			if (rect.left > (overflowOffset = $(_window).width() - this.$el.width() - 5)) {
				rect.left = overflowOffset;
			}
			offset = {
				left: rect.left,
				top: rect.bottom
			};
			if ((ref = this.context.callbacks("beforeReposition")) != null) {
				ref.call(this.context, offset);
			}
			this.$el.offset(offset);
			return this.context.trigger("reposition", [offset]);
		};

		View.prototype.next = function () {
			var cur, next, nextEl, offset;
			cur = this.$el.find('.cur').removeClass('cur');
			next = cur.next();
			if (!next.length) {
				next = this.$el.find('li:first');
			}
			next.addClass('cur');
			nextEl = next[0];
			offset = nextEl.offsetTop + nextEl.offsetHeight + (nextEl.nextSibling ? nextEl.nextSibling.offsetHeight : 0);
			return this.scrollTop(Math.max(0, offset - this.$el.height()));
		};

		View.prototype.prev = function () {
			var cur, offset, prev, prevEl;
			cur = this.$el.find('.cur').removeClass('cur');
			prev = cur.prev();
			if (!prev.length) {
				prev = this.$el.find('li:last');
			}
			prev.addClass('cur');
			prevEl = prev[0];
			offset = prevEl.offsetTop + prevEl.offsetHeight + (prevEl.nextSibling ? prevEl.nextSibling.offsetHeight : 0);
			return this.scrollTop(Math.max(0, offset - this.$el.height()));
		};

		View.prototype.scrollTop = function (scrollTop) {
			var scrollDuration;
			scrollDuration = this.context.getOpt('scrollDuration');
			if (scrollDuration) {
				return this.$elUl.animate({
					scrollTop: scrollTop
				}, scrollDuration);
			} else {
				return this.$elUl.scrollTop(scrollTop);
			}
		};

		View.prototype.show = function () {
			var rect;
			if (this.stopShowing) {
				this.stopShowing = false;
				return;
			}
			if (!this.visible()) {
				this.$el.show();
				this.$el.scrollTop(0);
				this.context.trigger('shown');
			}
			if (rect = this.context.rect()) {
				return this.reposition(rect);
			}
		};

		View.prototype.hide = function (e, time) {
			var callback;
			if (!this.visible()) {
				return;
			}
			if (isNaN(time)) {
				this.$el.hide();
				return this.context.trigger('hidden', [e]);
			} else {
				callback = (function (_this) {
					return function () {
						return _this.hide();
					};
				})(this);
				clearTimeout(this.timeoutID);
				return this.timeoutID = setTimeout(callback, time);
			}
		};

		View.prototype.render = function (list) {
			var $li, $ul, i, item, len, li, tpl;
			if (!($.isArray(list) && list.length > 0)) {
				this.hide();
				return;
			}
			this.$el.find('ul').empty();
			$ul = this.$el.find('ul');
			tpl = this.context.getOpt('displayTpl');

			for (i = 0, len = list.length; i < len; i++) {
				item = list[i];
				item = $.extend({}, item, {
					'atwho-at': this.context.at
				});
				li = this.context.callbacks("tplEval").call(this.context, tpl, item, "onDisplay");
				$li = $(this.context.callbacks("highlighter").call(this.context, li, this.context.query.text));
				$li.data("item-data", item);
				$ul.append($li);
			}
			this.show();
			if (this.context.getOpt('highlightFirst')) {
				return $ul.find("li:first").addClass("cur");
			}
		};

		return View;
	})();

	var Api;

	Api = {
		load: function (at, data) {
			var c;
			if (c = this.controller(at)) {
				return c.model.load(data);
			}
		},
		isSelecting: function () {
			var ref;
			return !!((ref = this.controller()) != null ? ref.view.visible() : void 0);
		},
		hide: function () {
			var ref;
			return (ref = this.controller()) != null ? ref.view.hide() : void 0;
		},
		reposition: function () {
			var c;
			if (c = this.controller()) {
				return c.view.reposition(c.rect());
			}
		},
		setIframe: function (iframe, asRoot) {
			this.setupRootElement(iframe, asRoot);
			return null;
		},
		run: function () {
			return this.dispatch();
		},
		destroy: function () {
			this.shutdown();
			return this.$inputor.data('atwho', null);
		}
	};

	$.fn.atwho = function (method) {
		var _args, result;
		_args = arguments;
		result = null;
		this.filter('textarea, input, [contenteditable=""], [contenteditable=true]').each(function () {
			var $this, app;
			if (!(app = ($this = $(this)).data("atwho"))) {
				$this.data('atwho', (app = new App(this)));
			}
			if (typeof method === 'object' || !method) {
				return app.reg(method.at, method);
			} else if (Api[method] && app) {
				return result = Api[method].apply(app, Array.prototype.slice.call(_args, 1));
			} else {
				return $.error("Method " + method + " does not exist on jQuery.atwho");
			}
		});

		if (result != null) {
			return result;
		} else {
			return this;
		}
	};

	$.fn.atwho["default"] = {
		at: void 0,
		alias: void 0,
		data: null,
		displayTpl: "<li>${name}</li>",
		insertTpl: "${atwho-at}${name}",
		headerTpl: null,
		callbacks: DEFAULT_CALLBACKS,
		searchKey: "name",
		suffix: void 0,
		hideWithoutSuffix: false,
		startWithSpace: true,
		acceptSpaceBar: false,
		highlightFirst: true,
		limit: 5,
		maxLen: 20,
		minLen: 0,
		displayTimeout: 300,
		delay: null,
		spaceSelectsMatch: false,
		tabSelectsMatch: true,
		editableAtwhoQueryAttrs: {},
		scrollDuration: 150,
		suspendOnComposing: true,
		lookUpOnClick: true
	};

	$.fn.atwho.debug = false;

}));
